#!/usr/bin/env ruby
# Copyright (C) 2012-2014 Cisco, Inc.

require 'json'
require 'ostruct'

require 'panoptimon/util'

config = ARGV[0] ? JSON::parse(ARGV[0], {symbolize_names: true}) : {'interval' => 60}

$stdout.sync = true

def GB b; (b.to_f / 1024**3).round(6); end # GB significant down to 4kB

class Array; def to_h; Hash[self]; end; end

get_mounts = ->() {
  skip_dev = ((config[:skip_dev]||[]) +
    %w[none udev proc mnttab objfs ctfs cgroup
    ]).map {|t| [t,true]}.to_h
  skip_type = ((config[:skip_type]||[]) +
    %w[devpts sysfs tmpfs swap lxproc lofs udf iso9660
    ]).map {|t| [t,true]}.to_h
  mtab = Panoptimon::Util.os('-option' => config,
    linux: '/etc/mtab', solaris: '/etc/mnttab')

  File.open(mtab, "r").read.split("\n").
    map{|l| l.split(/\s+/).values_at(1,0,2)}.
    keep_if {|p| not(skip_dev[p[1]]) and not(skip_type[p[2]])}.
    map {|p| p[0..1]}.to_h
}

def stat_fs (mounts)
  s = %x[stat --printf="%n %S %b %f %a %c %d\n" -f #{mounts.keys.join(' ')}].split("\n")
  s.map!{|l| l.split}
  s.map{|l|
    OpenStruct.new(
      name: mounts[l[0]],
      block_size: l[1].to_i,
      blocks: l[2].to_i,
      blocks_free: l[3].to_i,
      blocks_available: l[4].to_i, #blocks available to non-superuser
      files: l[5].to_i,
      files_available: l[6].to_i
    ).freeze
  }
end

while(true) do
  mounts = get_mounts[]
  stats = stat_fs(mounts).select{|m| m.blocks > 0}
  info = stats.map{ |s|
    [s.name, {
      space_used: GB((s.blocks - s.blocks_free) * s.block_size),
      space_free: GB(s.blocks_available         * s.block_size),
      space_priv: GB((s.blocks_free - s.blocks_available) * s.block_size),
      space_used_percentage: ((s.blocks - s.blocks_free) * 100.0 / (s.blocks - s.blocks_free + s.blocks_available)).round(2),
      files_used: s.files - s.files_available,
      files_free: s.files_available,
    }]
  }
  puts JSON::generate(Hash[*info.flatten])

  break unless config.include?('interval')
  sleep config['interval']
end
